package com.bana.sys.socket;

import com.bana.sys.code.SendResult;
import com.bana.sys.protocol.Protocol;
import com.bana.sys.utils.Convert;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.socket.DatagramPacket;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.net.InetSocketAddress;
import java.util.*;

/**
 * 执行字节流操作
 */
public abstract class SocketChannel extends ChannelInboundHandlerAdapter {


    private static Logger logger= LogManager.getLogger(ServerChannel.class);

    /**
     * 缓冲区最大长度
     */
    private static final int MaxBufferLength=10240;

    /**
     * 异步通知集合
     */
    private List<NoticeHandler> noticeHandlers = new ArrayList<>();

    /**
     * 存放半包的缓冲
     */
    private byte[] buffer = null;

    /**
     * 当前通道需要响应的协议
     */
    private Map<Integer, Protocol> protocols = new HashMap<>();

    /**
     * 添加需要响应的协议
     *
     * @param protocol 协议
     */
    public void addProtocol(Protocol protocol) {
        protocols.put((int) protocol.getId(), protocol);
    }

    /**
     * 添加异步通知
     *
     * @param handler 异步通知类
     */

    protected void addAsyncHandler(NoticeHandler handler)
    {
        noticeHandlers.add(handler);
    }


    /**
     * 发送协议
     * @param channel udp通道
     * @param address 发送地址
     * @param protocolId 协议编号
     * @param timeStamp 时间戳
     * @param buffer 发送字节流
     * @param timeout 超时时间
     * @return 第一个参数表示发送结果，第二个参数表示接收字节流
     */
    public SocketResult sendUpd(Channel channel, InetSocketAddress address, int protocolId,long timeStamp,int port,int controllerId,int controllerProtocolId, byte[] buffer,int timeout)
    {

        //异步回调
        NoticeHandler handler=new NoticeHandler(protocolId,timeStamp,port,controllerId,controllerProtocolId);

        //注册异步等待
        addAsyncHandler(handler);

        //发送数据
        SendResult result= sendUpd(channel,address,buffer);
        SocketResult sr=new SocketResult();
        sr.setResult(result);
        if(result==SendResult.Success)
        {
            //等待响应
            if (handler.await(timeout))
            {
                sr.setReceive(handler.getBuffer());
            }
            else
            {
                sr.setResult(SendResult.Timeout);
            }
        }
        return sr;

    }

    /**
     * 发送字节流
     *
     * @param buffer 协议内容
     * @return 返回true表示发送成功
     */
    protected SendResult sendUpd(Channel channel, InetSocketAddress address, byte[] buffer) {

        try
        {
            logger.info("- "+buffer.length+" "+address.toString()+" "+ Convert.toHex(buffer));
            ByteBuf buf= Unpooled.wrappedBuffer(buffer);
            channel.writeAndFlush(new DatagramPacket(buf, address));
            return SendResult.Success;
        }
        catch (Exception e)
        {
            logger.info(e);
            return SendResult.SendFailed;
        }
    }



    /**
     * 发送字节流
     *
     * @param buffer 协议内容
     * @return 返回true表示发送成功
     */
    protected SendResult sendTcp(ChannelHandlerContext ctx, byte[] buffer)
    {
        try
        {
            logger.info("- "+buffer.length+" "+ Convert.toHex(buffer));
            ByteBuf buf = ctx.alloc().buffer(buffer.length);
            buf.writeBytes(buffer);
            ctx.write(buf);
            ctx.flush();
            return SendResult.Success;
        } catch (Exception e) {
            logger.info(e);
            return SendResult.SendFailed;
        }
    }

    protected void receive(ChannelHandlerContext ctx, ByteBuf buf){

        byte[] buffer = new byte[buf.readableBytes()];
        try {
            buf.readBytes(buffer);
            buf.release();
            logger.info("+ " + buffer.length + " " + Convert.toHex(buffer));

            //如果有半包，先合并
            if (this.buffer != null) {
//                //协议超长错误
//                if (this.buffer.length + buffer.length > MaxBufferLength) {
//                    logger.warn("too long protocol");
//                    this.buffer = null;
//                    return;
//                }
                byte[] temp = new byte[this.buffer.length + buffer.length];
                System.arraycopy(this.buffer, 0, temp, 0, this.buffer.length);
                System.arraycopy(buffer, 0, temp, this.buffer.length, buffer.length);
                buffer = temp;
                this.buffer = null;
            }

            for (int i = 0; i < buffer.length; ) {
                //找帧头
                if (buffer[i] == (byte) 0xAB) {
                    //判断长度是否够帧头
                    if (i + Protocol.HeadSize > buffer.length) {
                        this.buffer = new byte[buffer.length - i];
                        System.arraycopy(buffer, i, this.buffer, 0, buffer.length - i);
                        return;
                    } else {
                        int id = Convert.toShort(buffer, i + 1);
                        int size = Convert.toShort(buffer, i + 3);
                        long timeStamp = Convert.toLong(buffer, i + 5);

                        //判断长度是否够帧头+协议长度
                        if (i + Protocol.HeadSize + size > buffer.length) {
                            this.buffer = new byte[buffer.length - i];
                            System.arraycopy(buffer, i, this.buffer, 0, buffer.length - i);
                            return;
                        } else {
                            byte[] protocol = new byte[size];
                            System.arraycopy(buffer, i + Protocol.HeadSize, protocol, 0, size);

                            //如何是请求协议则响应
                            if(protocols.containsKey(id))
                            {
                                byte[] responseBuffer=protocols.get(id).response(ctx,protocol,timeStamp);
                                if(responseBuffer!=null)
                                {
                                    sendTcp(ctx,responseBuffer);
                                }
                            }
                            //如果是响应协议则查看是否有在等待的
                            else {
                                //查看是否有需要通知的异步等待
                                for (int j = noticeHandlers.size() - 1; j >= 0; --j) {
                                    NoticeHandler asyncHandler = noticeHandlers.get(j);
                                    //异步等待会在超时或者第二次轮询时删除
                                    if (asyncHandler.getIsCompleted()) {
                                        noticeHandlers.remove(j);
                                    }
                                    //比对等待的集群协议和发送时的时间戳
                                    else if (asyncHandler.getProtocolId() == id) {
                                        if(id==0x04)
                                        {
                                            int port= Convert.toShort(protocol, 1);
                                            long controllerId=Convert.toInt(protocol,3);
                                            long protocolId=Convert.toInt(protocol,7);
                                            if(asyncHandler.getPort()==port
                                                    &&asyncHandler.getControllerId()==controllerId
                                                    &&asyncHandler.getControllerProtocolId()==protocolId)
                                            {
                                                asyncHandler.handle(protocol);
                                            }
                                        }
                                        else
                                        {
                                            asyncHandler.handle(protocol);
                                        }
                                    }
                                }
                            }
                            i = i + Protocol.HeadSize + size;
                        }
                    }
                } else {
                    ++i;
                }
            }
        } catch (Exception ex) {
            logger.info(ex);
        }
    }

}
